﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Runtime.CompilerServices;

namespace Pg.BioMedics.SDR.ServiceAgents
{
    /// <summary>
    /// ASR agent observer
    /// </summary>
    public sealed class ASRObserver
    {
        #region Constants

        public const int DefaultInterval = 20000;

        #endregion

        #region Fields

        private static ASRObserver instance;

        private int interval = DefaultInterval;
        private Thread monitor;
        private bool running = false;

        #endregion

        #region Constructors

        /// <summary>
        /// Creates instance of ASRObserver
        /// </summary>
        private ASRObserver() { }

        #endregion

        #region Static properties

        /// <summary>
        /// Gets ASRObserver instance
        /// </summary>
        public static ASRObserver Instance
        {
            get
            {
                if (instance == null)
                    instance = new ASRObserver();

                return instance;
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets / sets time interval between ASR service quering  
        /// </summary>
        public int Interval
        {
            get
            {
                return this.interval;
            }

            set
            {
                this.interval = value;
            }
        }

        /// <summary>
        /// Defines if all observer have been detached
        /// </summary>
        public bool ObserversDetached
        {
            get
            {
                return (TaskFinished == null) && (TaskFailed == null);
            }
        }

        /// <summary>
        /// Indincates if observation monitor is running
        /// </summary>
        public bool Running
        {
            get
            {
                return this.running;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Starts monitoring thread
        /// </summary>
        public void Start()
        {
            if (this.running)
                return;

            this.monitor = new Thread(new ThreadStart(Monitor));

            this.running = true;
            this.monitor.Start();
        }

        /// <summary>
        /// Stops monitoring thread
        /// </summary>
        public void Stop()
        {
            this.running = false;
        }

        /// <summary>
        /// Waits for pending monitor job
        /// </summary>
        public void WaitForPendingMonitor()
        {
            while (this.running)
                Thread.Sleep(5);
        }

        /// <summary>
        /// Provides logic for status check
        /// </summary>
        [MethodImpl(MethodImplOptions.Synchronized)]
        public void CheckStatus()
        {
            // notify about statis checking
            if (StatusChecking != null)
                StatusChecking(this, EventArgs.Empty);

            // check remote service
            using (ServiceAgentsContext serviceAgentsContext = new ServiceAgentsContext())
            {
                if (TaskFinished != null)
                {
                    foreach (Guid finishedTaskUniqueId in serviceAgentsContext.ASRAgent.GetFinishedTasks())
                    {
                        string result = serviceAgentsContext.ASRAgent.GetResult(finishedTaskUniqueId);

                        ASRObserverEventArgs args = new ASRObserverEventArgs() { AsrTaskUniqueId = finishedTaskUniqueId, Result = result };
                        TaskFinished(this, args);

                        if (args.Dispose)
                            serviceAgentsContext.ASRAgent.DisposeTask(finishedTaskUniqueId);                        
                    }
                }

                if (TaskFailed != null)
                {
                    foreach (Guid failedTaskUniqueId in serviceAgentsContext.ASRAgent.GetFailedTasks())
                    {
                        string result = serviceAgentsContext.ASRAgent.GetResult(failedTaskUniqueId);

                        ASRObserverEventArgs args = new ASRObserverEventArgs() { AsrTaskUniqueId = failedTaskUniqueId, Result = result };
                        TaskFailed(this, args);

                        if (args.Dispose)
                            serviceAgentsContext.ASRAgent.DisposeTask(failedTaskUniqueId);                        
                    }
                }
            }  
          
            // notify about check status finish
            if (StatusChecked != null)
                StatusChecked(this, EventArgs.Empty);
        }

        #endregion

        #region Events

        public event EventHandler StatusChecking;
        public event EventHandler StatusChecked;
        public event EventHandler<ASRObserverEventArgs> TaskFinished;
        public event EventHandler<ASRObserverEventArgs> TaskFailed;

        #endregion

        #region Support methods

        /// <summary>
        /// Monitoring routine
        /// </summary>
        private void Monitor()
        {
            while (this.running)
            {
                try
                {
                    CheckStatus();
                }
                catch { }

                // sleep for the time interval
                int sleepInterval = this.interval;
                Thread.Sleep(sleepInterval);
            }
        }

        #endregion
    }

    public class ASRObserverEventArgs : EventArgs
    {
        #region Properties

        /// <summary>
        /// Gets / sets ASR task unique id
        /// </summary>
        public Guid AsrTaskUniqueId
        {
            get;
            internal set;
        }

        /// <summary>
        /// Gets / sets ASR task result (transcription / lattice / mpeg-7 doc) or error message
        /// </summary>
        public string Result
        {
            get;
            internal set;
        }

        /// <summary>
        /// Defines if context should dispose this task 
        /// after return from notification method
        /// </summary>
        public bool Dispose
        {
            get;
            set;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Defines that observer should call task dispose on 
        /// ASR service
        /// </summary>
        public void DisposeTask()
        {
            Dispose = true;
        }

        #endregion
    }
}
